//=============================================================================
//
//		Copyright (C) 1995, 1996 by Paul S. McCarthy and Eric Sunshine.
//				Written by Paul S. McCarthy and Eric Sunshine.
//							All Rights Reserved.
//
//		This notice may not be removed from this source code.
//
//		This object is included in the MiscKit by permission from the authors
//		and its use is governed by the MiscKit license, found in the file
//		"License.rtf" in the MiscKit distribution.	Please refer to that file
//		for a list of all applicable permissions and restrictions.
//
//=============================================================================
//-----------------------------------------------------------------------------
// MiscTableScrollSort.M
//
//		Sorting support for MiscTableScroll.
//
// FIXME: OPTIMIZATION -- Don't make a function call to do the comparisons
//		unless the user has installed a custom sort function.  Use a big
//		switch statement.  (This eliminates one function call per comparison.)
//
// FIXME: OPTIMIZATION -- Pre-compute the method that will be used to access
//		the values in each slot.  We should not need to keep testing whether
//		or not the delegate / dataDelegate -respondTo: tableScroll:intValueAt::
//		for instance.  Figure this stuff out ahead of time, and store the
//		information in the MiscSlotSortInfo structure somewhere.  (This can
//		eliminate somewhere around 4-6 function calls per comparison.)
// 
// FIXME: OPTIMIZATION -- Consider re-using the sort info objects at least to 
//		avoid allocating and freeing entry_info[] and buff[] all the time.	
//		Consider embedding one of them right in the table-scroll itself.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// $Id: MiscTableScrollSort.M,v 1.2 96/02/12 00:06:06 sunshine Exp $
// $Log:		MiscTableScrollSort.M,v $
// Revision 1.2	 96/02/12  00:06:06	 sunshine
// Fixed bug where -border:setSlot:sortType: was ignoring case-sensitive
// title, case-insensitive title, state, and unsigned state sort types.	 This
// resulted in the default -stringValue variation to be used for those slots,
// which is clearly incorrect.
// 
// Revision 1.1	 96/01/13  23:31:59	 zarnuk
// Split-off from MiscTableScroll to ease maintenance.
// Now pre-computes more sorting information.
// Now has builtin median-of-three quick sort function.
// (Much closer to being re-entrant and thread-safe now).
// Now uses cell coordinates instead of cell-pointers for cell-wise compare.
// Now handles single-buffered lazy tables.	 (bug fix).
// Now uses ...ValueAt:: methods to enhance performance of lazy tables.
// Added new methods: -slotsAreSorted:, -border:slotIsSorted:,
//	   -border:compareSlots::
//-----------------------------------------------------------------------------
#import <misckit/MiscTableScroll.h>
#import "MiscTableBorder.h"

extern "C" {
#import <assert.h>
#import <stdlib.h>
#import <strings.h>
}

//=============================================================================
// CELL-VALUE ACCESS
//=============================================================================

//-----------------------------------------------------------------------------
// copy_string
//-----------------------------------------------------------------------------
static char* copy_string( char const* s, MiscSlotSortInfo* info )
	{
	int len = strlen( s ) + 1;
	int sz = info->buff_size;
	char* t = info->buff;
	if (sz < len)
		{
		do { sz += 256; } while (sz < len);
		NXZone* const z = info->zone;
		if (t == 0)
			t = (char*) NXZoneMalloc( z, sz );
		else
			t = (char*) NXZoneRealloc( z, t, sz );
		info->buff_size = sz;
		info->buff = t;
		}
	memcpy( t, s, len );
	return t;
	}


//-----------------------------------------------------------------------------
// cell_str
//-----------------------------------------------------------------------------
static inline char const* cell_str( int r, int c,
				MiscSlotSortInfo* info, BOOL make_copy )
	{
	char const* t = [info->table_scroll stringValueAt:r:c];
	if (t == 0)
		t = "";
	else if (make_copy)
		t = copy_string( t, info );
	return t;
	}


//-----------------------------------------------------------------------------
// cell_title
//-----------------------------------------------------------------------------
static inline char const* cell_title( int r, int c,
				MiscSlotSortInfo* info, BOOL make_copy )
	{
	char const* t = [info->table_scroll titleAt:r:c];
	if (t == 0)
		t = "";
	else if (make_copy)
		t = copy_string( t, info );
	return t;
	}


//-----------------------------------------------------------------------------
// cell_int
//-----------------------------------------------------------------------------
static inline int cell_int( int r, int c, MiscSlotSortInfo const* info )
	{
	return ([info->table_scroll intValueAt:r:c]);
	}


//-----------------------------------------------------------------------------
// cell_state
//-----------------------------------------------------------------------------
static inline int cell_state( int r, int c, MiscSlotSortInfo const* info )
	{
	return ([info->table_scroll stateAt:r:c]);
	}


//-----------------------------------------------------------------------------
// cell_tag
//-----------------------------------------------------------------------------
static inline int cell_tag( int r, int c, MiscSlotSortInfo const* info )
	{
	return ([info->table_scroll tagAt:r:c]);
	}


//-----------------------------------------------------------------------------
// cell_float
//-----------------------------------------------------------------------------
static inline float cell_float( int r, int c, MiscSlotSortInfo const* info )
	{
	return ([info->table_scroll floatValueAt:r:c]);
	}


//-----------------------------------------------------------------------------
// cell_double
//-----------------------------------------------------------------------------
static inline double cell_double( int r, int c, MiscSlotSortInfo const* info )
	{
	return ([info->table_scroll doubleValueAt:r:c]);
	}


//=============================================================================
// CELL COMPARISON FUNCTIONS
//=============================================================================

//-----------------------------------------------------------------------------
// cmp_ititle
//-----------------------------------------------------------------------------
static int cmp_ititle( int r1, int c1, int r2, int c2,
						MiscSlotSortInfo* info )
	{
	char const* const s1 = cell_title( r1,c1,info,info->need_copy );
	char const* const s2 = cell_title( r2,c2,info,NO );
	// FIXME: Use NXOrderStrings() here.
	return strcasecmp( s1, s2 );
	}


//-----------------------------------------------------------------------------
// cmp_title
//-----------------------------------------------------------------------------
static int cmp_title( int r1, int c1, int r2, int c2,
						MiscSlotSortInfo* info )
	{
	char const* const s1 = cell_title( r1,c1,info,info->need_copy );
	char const* const s2 = cell_title( r2,c2,info,NO );
	return strcmp( s1, s2 );
	}


//-----------------------------------------------------------------------------
// cmp_state
//-----------------------------------------------------------------------------
static int cmp_state( int r1, int c1, int r2, int c2,
						MiscSlotSortInfo const* info )
	{
	int const x1 = cell_state(r1,c1,info);
	int const x2 = cell_state(r2,c2,info);
	if (x1 < x2)
		return -1;
	if (x1 > x2)
		return 1;
	return 0;
	}


//-----------------------------------------------------------------------------
// cmp_ustate
//-----------------------------------------------------------------------------
static int cmp_ustate( int r1, int c1, int r2, int c2,
						MiscSlotSortInfo const* info )
	{
	unsigned int const x1 = (unsigned int) cell_state(r1,c1,info);
	unsigned int const x2 = (unsigned int) cell_state(r2,c2,info);
	if (x1 < x2)
		return -1;
	if (x1 > x2)
		return 1;
	return 0;
	}


//-----------------------------------------------------------------------------
// cmp_istr
//-----------------------------------------------------------------------------
static int cmp_istr( int r1, int c1, int r2, int c2,
						MiscSlotSortInfo* info )
	{
	char const* const s1 = cell_str( r1,c1,info,info->need_copy );
	char const* const s2 = cell_str( r2,c2,info,NO );
	// FIXME: Use NXOrderStrings() here.
	return strcasecmp( s1, s2 );
	}


//-----------------------------------------------------------------------------
// cmp_str
//-----------------------------------------------------------------------------
static int cmp_str( int r1, int c1, int r2, int c2,
						MiscSlotSortInfo* info )
	{
	char const* const s1 = cell_str( r1,c1,info,info->need_copy );
	char const* const s2 = cell_str( r2,c2,info,NO );
	return strcmp( s1, s2 );
	}


//-----------------------------------------------------------------------------
// cmp_int
//-----------------------------------------------------------------------------
static int cmp_int( int r1, int c1, int r2, int c2,
						MiscSlotSortInfo const* info )
	{
	int const x1 = cell_int(r1,c1,info);
	int const x2 = cell_int(r2,c2,info);
	if (x1 < x2)
		return -1;
	if (x1 > x2)
		return 1;
	return 0;
	}


//-----------------------------------------------------------------------------
// cmp_uint
//-----------------------------------------------------------------------------
static int cmp_uint( int r1, int c1, int r2, int c2,
						MiscSlotSortInfo const* info )
	{
	unsigned int const x1 = (unsigned int) cell_int(r1,c1,info);
	unsigned int const x2 = (unsigned int) cell_int(r2,c2,info);
	if (x1 < x2)
		return -1;
	if (x1 > x2)
		return 1;
	return 0;
	}


//-----------------------------------------------------------------------------
// cmp_tag
//-----------------------------------------------------------------------------
static int cmp_tag( int r1, int c1, int r2, int c2,
						MiscSlotSortInfo const* info )
	{
	int const x1 = cell_tag(r1,c1,info);
	int const x2 = cell_tag(r2,c2,info);
	if (x1 < x2)
		return -1;
	if (x1 > x2)
		return 1;
	return 0;
	}


//-----------------------------------------------------------------------------
// cmp_utag
//-----------------------------------------------------------------------------
static int cmp_utag( int r1, int c1, int r2, int c2,
						MiscSlotSortInfo const* info )
	{
	unsigned int const x1 = (unsigned int) cell_tag(r1,c1,info);
	unsigned int const x2 = (unsigned int) cell_tag(r2,c2,info);
	if (x1 < x2)
		return -1;
	if (x1 > x2)
		return 1;
	return 0;
	}


//-----------------------------------------------------------------------------
// cmp_float
//-----------------------------------------------------------------------------
static int cmp_float( int r1, int c1, int r2, int c2,
						MiscSlotSortInfo const* info )
	{
	float const x1 = cell_float(r1,c1,info);
	float const x2 = cell_float(r2,c2,info);
	if (x1 < x2)
		return -1;
	if (x1 > x2)
		return 1;
	return 0;
	}


//-----------------------------------------------------------------------------
// cmp_double
//-----------------------------------------------------------------------------
static int cmp_double( int r1, int c1, int r2, int c2,
						MiscSlotSortInfo const* info )
	{
	double const x1 = cell_double(r1,c1,info);
	double const x2 = cell_double(r2,c2,info);
	if (x1 < x2)
		return -1;
	if (x1 > x2)
		return 1;
	return 0;
	}


//-----------------------------------------------------------------------------
// cmp_skip
//-----------------------------------------------------------------------------
static int cmp_skip( int,int,int,int,MiscSlotSortInfo const*)
	{
	return 0;
	}


//-----------------------------------------------------------------------------
// COMPARE_FUNC[]
//-----------------------------------------------------------------------------
static MiscCompareEntryFunc const COMPARE_FUNC[ MISC_SORT_TYPE_MAX + 1 ] =
		{
		cmp_istr,
		cmp_str,
		cmp_int,
		cmp_uint,
		cmp_tag,
		cmp_utag,
		cmp_float,
		cmp_double,
		cmp_skip,
		cmp_ititle,
		cmp_title,
		cmp_state,
		cmp_ustate
		};


//=============================================================================
// SLOT-COMPARE
//=============================================================================
//-----------------------------------------------------------------------------
// MiscDefaultCompareSlotFunc
//-----------------------------------------------------------------------------
int MiscDefaultCompareSlotFunc(
		int slot1,
		int slot2,
		MiscSlotSortInfo* info )
	{
	int rc = 0;
	MiscEntrySortInfo const* p = info->entry_info;
	MiscEntrySortInfo const* const plim = p + info->num_entries;

	if (info->border_type == MISC_COL_BORDER)			// Row-wise compare
		{
		for ( ; p < plim; p++)
			{
			int const col = p->slot;
			if ((rc = (*(p->compare_func))(slot1,col,slot2,col,info)) != 0)
				return (p->ascending ? rc : -rc);
			}
		}
	else												// Col-wise compare
		{
		for ( ; p < plim; p++)
			{
			int const row = p->slot;
			if ((rc = (*(p->compare_func))(row,slot1,row,slot2,info)) != 0)
				return (p->ascending ? rc : -rc);
			}
		}

	return 0;
	}



//=============================================================================
// QSORT
//=============================================================================

//-----------------------------------------------------------------------------
// swap
//-----------------------------------------------------------------------------
inline static void swap( int x, int y, int a[] )
	{
	int t = a[x];
	a[x] = a[y];
	a[y] = t;
	}


//-----------------------------------------------------------------------------
// do_qsort
//-----------------------------------------------------------------------------
static void do_qsort(
		int a[],
		int N,
		MiscCompareSlotFunc f,
		MiscSlotSortInfo* info )
	{
	assert( N > 1 );

	int const STACK_MAX = 64;			// log_base_2(ULNG_MAX) * 2
	int stk[ STACK_MAX ];
	int top = 0;
	int left = 0;
	int right = N - 1;					// WARNING: N == 0
	int i,j,n;							// minus one is HUGE value.

	for (;;)
		{
		while (right > left)
			{
			n = (right - left) + 1;		// right,left bounds are inclusive.
			if (n >= 3)
				{
				int mid = (left + right) >> 1;
				if ((*f)(a[left],a[right],info) > 0) swap(left,right,a);
				if ((*f)(a[mid], a[right],info) > 0) swap(mid,right,a);
				if ((*f)(a[left],a[mid],  info) > 0) swap(left,mid,a);
				if (n == 3) break;
				right--;
				swap(mid,right,a);
				int v = a[right];
				i = left;
				j = right;
				for (;;)
					{
					while ((*f)(a[++i],v,info) < 0) /* empty */;
					while ((*f)(a[--j],v,info) > 0) /* empty */;
					if (i >= j) break;
					swap( i, j, a );
					}
				swap( i, right, a );
				right++;
				if (i - left > right - i)
					{ stk[top++] = left;  stk[top++] = i-1; left = i+1; }
				else if (right - i > 1)
					{ stk[top++] = i+1; stk[top++] = right; right = i-1; }
				else
					break;
				}
			else // (n < 3)
				{
				if (n > 1) // && (n < 3)
					{
					if ((*f)(a[left],a[right],info) > 0)
						swap(left,right,a);
					}
				break;
				}
			}
		if (top == 0) break;
		right = stk[--top];
		left = stk[--top];
		}
	}


@implementation MiscTableScroll(Sort)

//-----------------------------------------------------------------------------
// - compareSlotFunc
//-----------------------------------------------------------------------------
- (MiscCompareSlotFunc) compareSlotFunc
	{
	return sort_slot_func ? sort_slot_func : MiscDefaultCompareSlotFunc;
	}


//-----------------------------------------------------------------------------
// - setCompareSlotFunc:
//-----------------------------------------------------------------------------
- (void) setCompareSlotFunc: (MiscCompareSlotFunc) f
	{
	sort_slot_func =  (f ? f : MiscDefaultCompareSlotFunc);
	}


//-----------------------------------------------------------------------------
// - sortInfoInit:border:
//-----------------------------------------------------------------------------
- (void) sortInfoInit:(MiscSlotSortInfo*)ip border:(MiscBorderType)b
	{
	MiscBorderType const ob = MISC_OTHER_BORDER(b);
	MiscTableBorder* const obp = info[ob]->border;
	NXZone* const z = [self zone];

	ip->table_scroll	= self;
	ip->zone			= z;
	ip->border_type		= ob;
	ip->num_entries		= 0;
	ip->entry_info		= 0;
	ip->need_copy		= lazy && ([self buffCount] < 2);
	ip->buff			= 0;
	ip->buff_size		= 0;

	int M;										// Number of "cols".
	int const* v = [self slotSortVector:ob len:&M];
	if (v == 0 || M == 0)
		{
		v = obp->getPMap();
		M = obp->count();
		}

	if (M > 0)
		{
		MiscEntrySortInfo* ep = (MiscEntrySortInfo*) 
								NXZoneMalloc( z, M * sizeof(*ep) );

		if (ep != 0)
			{
			ip->num_entries		= M;
			ip->entry_info		= ep;

			int j = 0;
			for (int i = 0; i < M; i++)
				{
				MiscEntrySortInfo& r = ep[j++];
				int n = (v ? v[i] : i);
				BOOL was_neg = (n < 0);
				if (was_neg)
					n = -n;
				r.slot = n;
				if ([self border:ob slotSortDirection:n]==MISC_SORT_DESCENDING)
					was_neg = !was_neg;
				r.ascending = !was_neg;
				if ((r.compare_func = [self border:ob slotSortFunc:n]) == 0)
					{
					MiscSortType t = [self border:ob slotSortType:n];
					if ((unsigned int)t <= (unsigned int)MISC_SORT_TYPE_MAX &&
						t != MISC_SORT_SKIP)
						{
						r.sort_type = t;
						r.compare_func = COMPARE_FUNC[t];
						}
					else // No custom function, and sort-type == skip.
						{
						j--;	// Do not include this slot.
						}
					}
				else
					r.sort_type = MISC_SORT_CUSTOM;
				}
			ip->num_entries = j;		// Number of non-skip slots.
			}
		else
			{
			[self error:"Memory allocation failure.\n"];
			}
		}
	}


//-----------------------------------------------------------------------------
// - sortInfoDone:
//-----------------------------------------------------------------------------
- (void) sortInfoDone:(MiscSlotSortInfo*)ip
	{
	if (ip->entry_info != 0)
		{						// Cast off const-ness.
		NXZoneFree( ip->zone, (MiscEntrySortInfo*) ip->entry_info );
		ip->entry_info = 0;
		}
	if (ip->buff != 0)
		{
		free( ip->buff );
		ip->buff = 0;
		ip->buff_size = 0;
		}
	}


//-----------------------------------------------------------------------------
// - sortSlots:
//-----------------------------------------------------------------------------
- (void) sortSlots:(MiscBorderType) b
	{
	int const N = [self numSlots:b];			// Number of "rows"

	if (N > 1)
		{
		MiscSlotSortInfo sort_info;
		[self sortInfoInit:&sort_info border:b];
		if (sort_info.num_entries > 0)
			{
			NXZone* const z = [self zone];
			int* const new_v2p = (int*) NXZoneMalloc( z, N * sizeof(int) );
			if (new_v2p != 0)
				{
				for (int j = 0; j < N; j++)
					new_v2p[j] = j;
	
				do_qsort( new_v2p, N, [self compareSlotFunc], &sort_info );

				info[b]->border->setPMap( new_v2p );

				[self update];
				NXZoneFree( z, new_v2p );
				}
			else
				{
				[self error:"Memory allocation failure.\n" ];
				}
			}
		[self sortInfoDone:&sort_info];
		}
	}

- (void) sortCols { [self sortSlots:MISC_COL_BORDER]; }
- (void) sortRows { [self sortSlots:MISC_ROW_BORDER]; }


//-----------------------------------------------------------------------------
// - border:compareSlots::info:
//-----------------------------------------------------------------------------
- (int) border:(MiscBorderType)b compareSlots:(int)slot1 :(int)slot2
		info:(MiscSlotSortInfo*)ip
	{
	return (*[self compareSlotFunc])( slot1, slot2, ip );
	}

- (int) compareCols:(int)c1 :(int)c2 info:(MiscSlotSortInfo*)ip
		{ return [self border:MISC_COL_BORDER compareSlots:c1:c2 info:ip]; }
- (int) compareRows:(int)r1 :(int)r2 info:(MiscSlotSortInfo*)ip
		{ return [self border:MISC_ROW_BORDER compareSlots:r1:r2 info:ip]; }


//-----------------------------------------------------------------------------
// - border:compareSlots::
//-----------------------------------------------------------------------------
- (int) border:(MiscBorderType)b compareSlots:(int)slot1 :(int)slot2
	{
	int rc = 0;
	MiscSlotSortInfo sort_info;
	[self sortInfoInit:&sort_info border:b];
	if (sort_info.num_entries > 0)
		rc = [self border:b compareSlots:slot1:slot2 info:&sort_info];
	[self sortInfoDone:&sort_info];
	return rc;
	}

- (int) compareCols:(int)c1 :(int)c2
		{ return [self border:MISC_COL_BORDER compareSlots:c1:c2]; }
- (int) compareRows:(int)r1 :(int)r2
		{ return [self border:MISC_ROW_BORDER compareSlots:r1:r2]; }


//-----------------------------------------------------------------------------
// - border:sortSlot:
//-----------------------------------------------------------------------------
- (BOOL) border:(MiscBorderType)b sortSlot:(int)pslot
	{
	BOOL moved = NO;

	int const N = [self numSlots:b];			// Number of "rows"

	if (N > 1)
		{
		MiscSlotSortInfo sort_info;
		[self sortInfoInit:&sort_info border:b];
		if (sort_info.num_entries > 0)
			{
			MiscCompareSlotFunc func = [self compareSlotFunc];

			int const vslot = [self border:b slotPosition:pslot];
			int const prev = (vslot > 0 ?
						[self border:b slotAtPosition:vslot - 1] : -1);
			int const next = (vslot < N - 1 ?
						[self border:b slotAtPosition:vslot + 1] : N);
	
			int lo = vslot;
			int hi = vslot - 1;
	
			if (prev >= 0 && (*func)( pslot, prev, &sort_info ) < 0)
				{
				lo = 0;
				hi = vslot - 2;
				}
			else if (next < N && (*func)( pslot, next, &sort_info) > 0)
				{
				lo = vslot + 1;
				hi = N - 1;
				}
	
			while (lo <= hi)	// Binary search.
				{
				int const mid = (lo + hi) >> 1;
				int const n = [self border:b slotAtPosition:mid];
				int const cmp = (*func)( pslot, n, &sort_info );
				if (cmp < 0)
					hi = mid - 1;
				else
					lo = mid + 1;
				}
	
			if (lo != vslot)
				{
				[self border:b moveSlotFrom:vslot to:lo];
				[self update];
				moved = YES;
				}
			}
		[self sortInfoDone:&sort_info];
		}

	return moved;
	}

- (BOOL) sortCol:(int)n { return [self border:MISC_COL_BORDER sortSlot:n]; }
- (BOOL) sortRow:(int)n { return [self border:MISC_ROW_BORDER sortSlot:n]; }


//-----------------------------------------------------------------------------
// - slotsAreSorted:
//-----------------------------------------------------------------------------
- (BOOL) slotsAreSorted:(MiscBorderType)b
	{
	BOOL sorted = YES;
	int const N = [self numSlots:b];			// Number of "rows".

	if (N > 1)
		{
		MiscSlotSortInfo data;
		[self sortInfoInit:&data border:b];
		if (data.num_entries > 0)
			{
			MiscCompareSlotFunc func = [self compareSlotFunc];
			for (int i = 1; i < N; i++)
				if ((*func)( i - 1, i, &data ) > 0)
					{
					sorted = NO;
					break;
					}
			}
		[self sortInfoDone:&data];
		}

	return sorted;
	}

- (BOOL) colsAreSorted	{ return [self slotsAreSorted:MISC_COL_BORDER]; }
- (BOOL) rowsAreSorted	{ return [self slotsAreSorted:MISC_ROW_BORDER]; }


//-----------------------------------------------------------------------------
// - border:slotIsSorted:
//-----------------------------------------------------------------------------
- (BOOL) border:(MiscBorderType)b slotIsSorted:(int)pslot
	{
	BOOL sorted = YES;
	int const N = [self numSlots:b];			// Number of "rows".

	if (N > 1)
		{
		MiscSlotSortInfo data;
		[self sortInfoInit:&data border:b];
		if (data.num_entries > 0)
			{
			MiscCompareSlotFunc func = [self compareSlotFunc];

			int const vslot = [self border:b slotPosition:pslot];
			int const prev = (vslot > 0 ?
						[self border:b slotAtPosition:vslot - 1] : -1);
			int const next = (vslot < N - 1 ?
						[self border:b slotAtPosition:vslot + 1] : N);

			if (prev >= 0 && (*func)( prev, pslot, &data ) > 0)
				sorted = NO;

			else if (next < N && (*func)( pslot, next, &data ) > 0)
				sorted = NO;
			}
		[self sortInfoDone:&data];
		}

	return sorted;
	}

- (BOOL) colIsSorted:(int)n
		{ return [self border:MISC_COL_BORDER slotIsSorted:n]; }
- (BOOL) rowIsSorted:(int)n
		{ return [self border:MISC_ROW_BORDER slotIsSorted:n]; }


//-----------------------------------------------------------------------------
// autoSort
//-----------------------------------------------------------------------------
- (BOOL) autoSortSlots:(MiscBorderType)b
		{ return info[b]->autoSort; }
- (void) border:(MiscBorderType)b setAutoSortSlots:(BOOL)flag
		{ info[b]->autoSort = flag; }

- (BOOL) autoSortCols
		{ return [self autoSortSlots:MISC_COL_BORDER]; }
- (void) setAutoSortCols:(BOOL)flag
		{ [self border:MISC_COL_BORDER setAutoSortSlots:flag]; }

- (BOOL) autoSortRows
		{ return [self autoSortSlots:MISC_ROW_BORDER]; }
- (void) setAutoSortRows:(BOOL)flag
		{ [self border:MISC_ROW_BORDER setAutoSortSlots:flag]; }


//-----------------------------------------------------------------------------
// Sort Vector
//-----------------------------------------------------------------------------
- (int const*) slotSortVector:(MiscBorderType)b len:(int*)len
		{
		MiscBorderInfo* const ip = info[b];
		*len = ip->sort_vector_len;
		return ip->sort_vector;
		}

- (void) border:(MiscBorderType)b setSlotSortVector:(int const*)v len:(int)n
		{
		NXZone* const z = [self zone];
		MiscBorderInfo* const ip = info[b];
		if (ip->sort_vector != 0)
			NXZoneFree( z, ip->sort_vector );
		if (n > 0)
			{
			int const nbytes = n * sizeof(*v);
			ip->sort_vector_len = n;
			ip->sort_vector = (int*) NXZoneMalloc( z, nbytes );
			memcpy( ip->sort_vector, v, nbytes );
			}
		else
			{
			ip->sort_vector_len = 0;
			ip->sort_vector = 0;
			}
		}

- (int const*) colSortVectorLen:(int*)len
		{ return [self slotSortVector:MISC_COL_BORDER len:len]; }
- (void) setColSortVector:(int const*)v len:(int)n;
		{ [self border:MISC_COL_BORDER setSlotSortVector:v len:n]; }

- (int const*) rowSortVectorLen:(int*)len
		{ return [self slotSortVector:MISC_ROW_BORDER len:len]; }
- (void) setRowSortVector:(int const*)v len:(int)n;
		{ [self border:MISC_ROW_BORDER setSlotSortVector:v len:n]; }



//-----------------------------------------------------------------------------
// Sort Func
//-----------------------------------------------------------------------------
- (MiscCompareEntryFunc) border:(MiscBorderType)b slotSortFunc:(int)n
		{ return info[b]->border->getSortFunc_P(n); }
- (void) border:(MiscBorderType)b setSlot:(int)n
				sortFunc:(MiscCompareEntryFunc)x
		{ info[b]->border->setSortFunc_P(n,x); }

- (MiscCompareEntryFunc) colSortFunc:(int)n
		{ return [self border:MISC_COL_BORDER slotSortFunc:n]; }
- (void) setCol:(int)n sortFunc:(MiscCompareEntryFunc)x
		{ [self border:MISC_COL_BORDER setSlot:n sortFunc:x]; }

- (MiscCompareEntryFunc) rowSortFunc:(int)n
		{ return [self border:MISC_ROW_BORDER slotSortFunc:n]; }
- (void) setRow:(int)n sortFunc:(MiscCompareEntryFunc)x
		{ [self border:MISC_ROW_BORDER setSlot:n sortFunc:x]; }


//-----------------------------------------------------------------------------
// Sort Direction
//-----------------------------------------------------------------------------
- (MiscSortDirection) border:(MiscBorderType)b slotSortDirection:(int)n
		{ return info[b]->border->getSortDirection_P(n); }
- (void) border:(MiscBorderType)b setSlot:(int)n
				sortDirection:(MiscSortDirection)x
		{
		if ((unsigned int) x <= (unsigned int) MISC_SORT_DESCENDING)
			info[b]->border->setSortDirection_P(n,x);
		}

- (MiscSortDirection) colSortDirection:(int)n
		{ return [self border:MISC_COL_BORDER slotSortDirection:n]; }
- (void) setCol:(int)n sortDirection:(MiscSortDirection)x
		{ [self border:MISC_COL_BORDER setSlot:n sortDirection:x]; }

- (MiscSortDirection) rowSortDirection:(int)n
		{ return [self border:MISC_ROW_BORDER slotSortDirection:n]; }
- (void) setRow:(int)n sortDirection:(MiscSortDirection)x
		{ [self border:MISC_ROW_BORDER setSlot:n sortDirection:x]; }


//-----------------------------------------------------------------------------
// Sort Type
//-----------------------------------------------------------------------------
- (MiscSortType) border:(MiscBorderType)b slotSortType:(int)n;
		{ return info[b]->border->getSortType_P(n); }
- (void) border:(MiscBorderType)b setSlot:(int)n
				sortType:(MiscSortType)x
		{
		if ((unsigned int) x <= (unsigned int) MISC_SORT_TYPE_MAX)
			info[b]->border->setSortType_P(n,x);
		}

- (MiscSortType) colSortType:(int)n
		{ return [self border:MISC_COL_BORDER slotSortType:n]; }
- (void) setCol:(int)n sortType:(MiscSortType)x
		{ [self border:MISC_COL_BORDER setSlot:n sortType:x]; }

- (MiscSortType) rowSortType:(int)n
		{ return [self border:MISC_ROW_BORDER slotSortType:n]; }
- (void) setRow:(int)n sortType:(MiscSortType)x
		{ [self border:MISC_ROW_BORDER setSlot:n sortType:x]; }

@end
